package com.foo.common.base.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import com.foo.common.base.annotations.JspCrud;
import com.foo.common.base.pojo.FooCrudField;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import com.google.common.base.CaseFormat;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Crud自动生成类，只匹配tenmalife-platform
 * 
 * @author Steve
 *
 */
public enum TenmalifeCrudHelper {
	INSTANCE;

	/**
	 * NOTE:下面四个域可以编辑
	 */
	private static Class<?> modelClazz = CommonCrudHelper.class;// 参考注解：com.foo.common.base.annotations.Crud，也可以参考该目录下的其他model类是怎么写的
	private static String modelUppercase = "ShopBrandSupplier";// model名称，大写字母开头的驼峰格式。
	private static String actionPath = "/shop/shopBrandSupplier";// action对应jsp的存放位置，比如/edu/course，那么jsp应该放在目录：/WEB-INF/pages/edu/course/目录下
	private static String jspTitleName = "品牌供应商管理";// jsp模块中文名称，用于页面显示，页面title依赖该字段的值
	private final static String global_auto_generated_directory_str = FooUtils
			.getGlobalAutoGeneratedDirectoryStr();
	private static final String filesGeneratedDirectoryStr = global_auto_generated_directory_str;// 文件生成的路径

	/**
	 * NOTE:以下所有域请不要编辑
	 */
	private static final String actionDirectoryStr = global_auto_generated_directory_str
			+ "\\action";
	private static final String mgrDirectoryStr = global_auto_generated_directory_str
			+ "\\mgr";
	private static String template_crud_java_action_mustache = "template-crud-java-action.mustache";// 请注意，该模板适用于tenmalife
	private static String template_crud_java_mgr_mustache = "template-crud-java-mgr.mustache";// 请注意，该模板适用于tenmalife
	private static String template_crud_java_mgr_impl_mustache = "template-crud-java-mgr-impl.mustache";// 请注意，该模板适用于tenmalife
	private static String template_crud_jsp_mustache = "template-crud-tenmalife.jsp";// 请注意，该模板适用于tenmalife
	private static String jspFileName;// 目前名称生成规则是以下划线来分割单词，例如在actionPath为/edu/course，并且modelClazz为EduCourse的情况下，jsp文件名为：edu_course_list.jsp
	private final static org.slf4j.Logger logger = org.slf4j.LoggerFactory
			.getLogger(TenmalifeCrudHelper.class);

	public static void main(String[] args) throws Exception {

		// 初始化template所有参数
		Map<String, Object> source = TenmalifeCrudHelper.initCrudeDateOnClass();

		// 检查所有参数是否合乎规范
		checkParams();

		String prefix = Splitter.on("/").omitEmptyStrings().limit(2)
				.splitToList(actionPath).get(0);

		source.put("modelPrefixUppercase", prefix);
		source.put("modelPrefixLowercase", prefix.toLowerCase());

		FooUtils.cleanDirectory(new File(filesGeneratedDirectoryStr));

		generateJavaWithMustache(source);
		generateJspWithMustache(source);

		// String myClassName =
		// CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,
		// modelUppercase);
		// LOGGER.info("{}", myClassName);

		logger.info("you should place all java class under dir:{}",
				"com.feiynn." + source.get("modelPrefixLowercase"));

		logger.info("you should place all jsp under dir:{}",
				"/tenmalife-platform/src/main/webapp/WEB-INF/pages/"
						+ actionPath);

	}

	private static void checkParams() {

	}

	public static <T> Map<String, Object> initCrudeDateOnClass() {
		// modelUppercase = modelClazz.getSimpleName();
		// jspFileName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,
		// modelUppercase) + "List.jsp";
		jspFileName = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE,
				modelUppercase) + "_list.jsp";

		FooCrudField fooCrudField = new FooCrudField();
		List<FooCrudField> original_list = Lists.newArrayList();

		JspCrud crudAnnotation = null;

		// 没有crud标签会被直接排除在外
		for (Field field : modelClazz.getDeclaredFields()) {

			if (!field.isAnnotationPresent(JspCrud.class)) {
				continue;
			}

			fooCrudField = new FooCrudField();

			crudAnnotation = field.getAnnotation(JspCrud.class);
			fooCrudField.setCnName(crudAnnotation.cnName());
			fooCrudField.setDisplayOnTable(crudAnnotation.displayOnTable());
			fooCrudField.setFormFieldType(crudAnnotation.formFieldType());
			fooCrudField.setValidateOnForm(crudAnnotation.validateOnForm());

			fooCrudField.setFieldName(field.getName());
			fooCrudField
					.setFieldType(FooUtils.getJavaClassFieldTypeString(field));
			original_list.add(fooCrudField);
		}

		// 用于dataTables显示的list
		List<FooCrudField> dataTables_list = Lists.newArrayList();
		for (FooCrudField tmp : original_list) {
			if (tmp.isDisplayOnTable()) {
				dataTables_list.add(tmp);
			}
		}

		// form表单字段,存放hidden域
		List<FooCrudField> form_hidden_list = Lists.newArrayList();

		// form表单字段,存放text域
		List<FooCrudField> form_text_list_ori = Lists.newArrayList();

		for (FooCrudField tmp : original_list) {
			if (tmp.getFormFieldType().equals("hidden")) {
				form_hidden_list.add(tmp);
			} else {
				form_text_list_ori.add(tmp);
			}
		}

		// form表单字段,存放2个一组的，用于表单布局
		List<Map<String, FooCrudField>> form_text_list = Lists.newArrayList();

		// text的form为2个一组，所以需要重新构造数据结构
		Map<String, FooCrudField> form_text_list_map = Maps.newHashMap();

		List<List<FooCrudField>> partitionList = Lists
				.partition(form_text_list_ori, 2);

		for (List<FooCrudField> tmp : partitionList) {
			form_text_list_map = Maps.newHashMap();
			form_text_list_map.put("left", tmp.get(0));
			if (tmp.size() >= 2) {
				form_text_list_map.put("right", tmp.get(1));
			}
			form_text_list.add(form_text_list_map);
		}

		Map<String, Object> bean = Maps.newHashMap();

		bean.put("modelUppercase", modelUppercase);
		bean.put("modelLowercase", CaseFormat.UPPER_CAMEL
				.to(CaseFormat.LOWER_CAMEL, modelUppercase));
		bean.put("jspTitleName", jspTitleName);
		bean.put("jspFileName", jspFileName);
		bean.put("original_list", original_list);
		bean.put("dataTables_list", dataTables_list);
		bean.put("form_hidden_list", form_hidden_list);
		bean.put("form_text_list", form_text_list);
		bean.put("form_text_list_ori", form_text_list_ori);
		bean.put("actionPath", actionPath);
		logger.info("result is :{}", bean);
		return bean;

	}

	@SuppressWarnings("unchecked")
	public static void generateJavaWithMustache(Map<String, Object> scopes)
			throws Exception {

		FooUtils.forceMkdir(actionDirectoryStr);
		FooUtils.forceMkdir(mgrDirectoryStr);

		MustacheFactory mf = new DefaultMustacheFactory();

		List<FooCrudField> original_list = (List<FooCrudField>) scopes
				.get("original_list");

		List<FooCrudField> get_and_set_list = Lists.newArrayList();

		String tmpFieldName;
		String tmpFieldNameCapital;
		for (FooCrudField tmp : original_list) {
			if (tmp.getFieldName().equals("id")) {
				continue;
			}

			tmpFieldName = tmp.getFieldName();
			tmpFieldNameCapital = StringUtils.capitalize(tmp.getFieldName());

			if (tmp.getFieldType().equals("Date")) {

				tmp.setFiledGetAndSetStr("model.set" + tmpFieldNameCapital
						+ "(Strings.nullToEmpty(request.getParameter(\""
						+ tmpFieldName + "\")));");

			} else if (tmp.getFieldType().equals("int")) {

				tmp.setFiledGetAndSetStr("model.set" + tmpFieldNameCapital
						+ "(Integer.parseInt(Strings.nullToEmpty(request.getParameter(\""
						+ tmpFieldName + "\"))));");

			} else if (tmp.getFieldType().equals("String")) {
				tmp.setFiledGetAndSetStr("model.set" + tmpFieldNameCapital
						+ "(Strings.nullToEmpty(request.getParameter(\""
						+ tmpFieldName + "\")));");
			} else if (tmp.getFieldType().equalsIgnoreCase("double")) {
				tmp.setFiledGetAndSetStr("model.set" + tmpFieldNameCapital
						+ "(Double.parseDouble(Strings.nullToEmpty(request.getParameter(\""
						+ tmpFieldName + "\"))));");
			} else {
				logger.error("error type on filed:{} for type:{}", tmpFieldName,
						tmp.getFieldType());
				throw new UnsupportedOperationException(
						"error type on field:" + tmpFieldName);
			}
			get_and_set_list.add(tmp);
		}

		scopes.put("get_and_set_list", get_and_set_list);

		Mustache mustache = mf.compile(template_crud_java_action_mustache);
		Writer writer = new StringWriter();
		mustache.execute(writer, scopes);
		writer.flush();
		String result = writer.toString();
		// LOGGER.info("result is: \n\n\n {}", result);
		File myFile = new File(
				actionDirectoryStr + "\\" + modelUppercase + "Action.java");
		IOUtils.write(result, new FileOutputStream(myFile), "utf-8");
		logger.info("generate java action file :{} on path:{}",
				myFile.getName(), filesGeneratedDirectoryStr);

		mustache = mf.compile(template_crud_java_mgr_mustache);
		writer = new StringWriter();
		mustache.execute(writer, scopes);
		writer.flush();
		result = writer.toString();
		myFile = new File(mgrDirectoryStr + "\\" + modelUppercase + "Mgr.java");
		IOUtils.write(result, new FileOutputStream(myFile), "utf-8");
		logger.info("generate java mgr file :{} on path:{}", myFile.getName(),
				mgrDirectoryStr);

		mustache = mf.compile(template_crud_java_mgr_impl_mustache);
		writer = new StringWriter();
		mustache.execute(writer, scopes);
		writer.flush();
		result = writer.toString();
		myFile = new File(
				mgrDirectoryStr + "\\" + modelUppercase + "MgrImpl.java");
		IOUtils.write(result, new FileOutputStream(myFile), "utf-8");
		logger.info("generate java mgr-impl file :{} on path:{}",
				myFile.getName(), mgrDirectoryStr);

	}

	public static void generateJspWithMustache(Map<String, Object> scopes)
			throws Exception {

		MustacheFactory mf = new DefaultMustacheFactory();

		Mustache mustache = mf.compile(template_crud_jsp_mustache);

		Writer writer = new StringWriter();

		mustache.execute(writer, scopes);
		writer.flush();

		String result = writer.toString();
		// LOGGER.info("result is: \n\n\n {}", result);

		FooUtils.forceMkdir(filesGeneratedDirectoryStr);
		File resultFile = new File(
				filesGeneratedDirectoryStr + "\\" + jspFileName);

		IOUtils.write(result, new FileOutputStream(resultFile), "utf-8");
		logger.info("generate file:{} on path:{}", resultFile.getName(),
				filesGeneratedDirectoryStr);
	}

}
